// $Id$
// Licensed under Apache License Version 2.0
// See: http://www.apache.org/licenses/LICENSE-2.0.html
package de.servicezombie.cxf.jaxb;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;

public class AnnotatedClassScanner {
	
	public Class<?>[] scan(Class<?>... types) {
		
		Set<Class<?>> allClasses = new HashSet<Class<?>>();

		for (Class<?> type : types) {
			
			allClasses.add(type);
			
			Field[] fields = type.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				
				List<Class<?>> foundTypes = findTypesOfField(fields[i]);
				
				for(Class<?> foundType : foundTypes) {
					allClasses.add(foundType);
				}
			}
		}

		Class<?>[] result = new Class<?>[allClasses.size()];
		result = allClasses.toArray(result);
		return result;
	}
	

	private List<Class<?>> findTypesOfField(Field field) {
		
		field.setAccessible(true);
		Class<?> requiredType = findByXmlElementAnnotation(field);
		
		List<Class<?>> res;
		
		if(requiredType != null) {
			res = new ArrayList<Class<?>>();
			res.add(requiredType);			
		}
		else {
			res = findByXmlElementsAnnotation(field);
		}
		
		return res;
	}

	private List<Class<?>> findByXmlElementsAnnotation(Field field) {
		XmlElements xmlElementsAnnotation = field.getAnnotation(XmlElements.class);
		if(xmlElementsAnnotation == null) {
			return Collections.emptyList();
		}
		
		List<Class<?>> res = new ArrayList<Class<?>>();
		XmlElement[] elements = xmlElementsAnnotation.value();
		
		for(XmlElement element : elements) {
			Class<?> type = element.type();
			if(type != null) {
				res.add(type);
			}
		}
		
		return res;
	}

	private Class<?> findByXmlElementAnnotation(Field field) {
		XmlElement xmlElementAnnotation = field.getAnnotation(XmlElement.class);
		if(xmlElementAnnotation == null) {
			return null;
		}
				
		Class<?> requiredType = xmlElementAnnotation.type();		
		if(requiredType == null) {
			requiredType = field.getType();
		}
		return requiredType;
	}

}
